package show.me.the.money.util.context;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings("unchecked")
public class HashMapContext extends Context {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5751391998973651463L;
	private HashMap<String, Object> data;
	private static Pattern pattern = Pattern.compile("(.+)\\[(\\d+)\\]");

	public HashMapContext(HashMap<String, Object> data) {
		super();
		this.data = data;
	}

	public boolean has(String name) {
		int n = name.indexOf(".");
		if (n > -1) {
			DiveResult r = dive(name);
			return r != null;
		} else {
			return data.containsKey(name);
		}
	}

	public void put(String name, Object value) {
		int n = name.indexOf(".");
		if (n > -1) {
			DiveResult r = dive(name);
			if (r != null) {
				r.setValue(value);
			}
		} else {
			data.put(name, value);
		}
	}

	public Object get(String name, HashMap<String, Object> arg) {
		int n = name.indexOf(".") + name.indexOf("[");
		if (n > -2) {
			DiveResult r = dive(name);
			if (r != null) {
				return r.getValue();
			}
			return null;
		}
		return data.get(name);
	}

	private DiveResult dive(String name) {
		String[] names = name.split("\\.");
		HashMap<String, Object> o = data;
		try {
			int n = names.length;
			for (int i = 0; i < n; i++) {
				String nm = names[i];
				// find if Array exist
				Matcher m = pattern.matcher(nm);
				String key = nm;
				List<Integer> indexes = new ArrayList();
				while (m.find()) {
					if (m.groupCount() == 2) {
						int index = Integer.parseInt(m.group(2));
						indexes.add(index);
						key = m.group(1);
					}
					m = pattern.matcher(m.group(1));
				}
				int size = indexes.size();
				if (size > 0) { // process arrary
					if (o.containsKey(key)) {
						int index = indexes.get(size - 1);
						if (i == n - 1) { // process last one
							if (size > 1) { // muti Array at last position
								List ls = (List) ((List) o.get(key)).get(index);
								for (int j = size - 2; j >= 0; j--) {
									index = indexes.get(j);
									if (j == 0) { // at the result point
										return new ArrayRST(ls, index);
									} else {
										ls = (List) ls.get(index);
									}
								}
							} else { // Single Array at the result point
								return new ArrayRST((List) o.get(key), index);
							}
						} else { // in the mid position,pass the token o;
							if (size > 1) { // muti Array at mid position
								List ls = (List) ((List) o.get(key)).get(index);
								for (int j = size - 2; j >= 0; j--) {
									index = indexes.get(j);
									if (j == 0) {
										o = (HashMap<String, Object>) ls
												.get(index);
									} else {
										ls = (List) ls.get(index);
									}
								}
							} else { // Single Array at mid position
								o = (HashMap<String, Object>) ((List) o
										.get(key)).get(index);
							}
						}
					}
				} else { // process JsonObject
					if (i == n - 1) {// at the result point
						return new ObjectRST(o, nm);
					} else {
						o = (HashMap<String, Object>) o.get(nm);
					}
				}
			}
			return null;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public String value(String name, HashMap<String, Object> arg) {
		return String.valueOf(get(name, arg));
	}

	public static void main(String[] args) {
		HashMap<String, Object> h = new HashMap<String, Object>();

		List ls = new ArrayList();
		ls.add("a");
		ls.add("b");
		ls.add("c");

		List ls2 = new ArrayList();
		ls2.add("a1");
		ls2.add("b2");
		ls2.add("c3");

		ls.add(ls2);

		h.put("name", "sean");

		HashMap<String, Object> h2 = new HashMap<String, Object>();
		h2.put("h21", "21");
		h2.put("h22", "22");
		h.put("arrs", ls);

		h.put("h2", h2);

		Context ctx = new HashMapContext(h);
		System.out.println(ctx.value("arrs[3][2]"));
		System.out.println(ctx.value("h2.h22"));

	}
}

interface DiveResult {
	Object getValue();

	void setValue(Object v);
}

class ObjectRST implements DiveResult {
	private HashMap<String, Object> o;
	private String name;

	public ObjectRST(HashMap<String, Object> o, String n) {
		this.o = o;
		name = n;
	}

	public Object getValue() {
		return o.get(name);
	}

	public void setValue(Object v) {
		o.put(name, v);
	}
}

@SuppressWarnings("unchecked")
class ArrayRST implements DiveResult {
	private List ls;
	private int index;

	public ArrayRST(List ls, int i) {
		this.ls = ls;
		index = i;
	}

	public Object getValue() {
		return ls.get(index);
	}

	public void setValue(Object v) {
		ls.add(index, v);
	}

}
